iT邦幫忙

2

同時搞定TensorFlow、PyTorch (三) :資料前置處理

  • 分享至 

  • xImage
  •  
  1. 同時搞定TensorFlow、PyTorch (一):梯度下降
  2. 同時搞定TensorFlow、PyTorch (二):模型定義
  3. 同時搞定TensorFlow、PyTorch (三) :資料前置處理
  4. 同時搞定TensorFlow、PyTorch (四):模型訓練

前言

上一篇談到神經層及神經網路模型的定義,這一次我們就來研究 TensorFlow/PyTorch 如何進行資料前置處理,並訓練模型。

資料前置處理

基本上,TensorFlow、PyTorch 都提供NumPy格式相容的資料型態,不管是影像、文字或語音,依程序是讀取資料檔後轉為 NumPy 陣列,經過前置處理再餵入模型,但實務上不會一次載入所有資料,因為記憶體會爆掉,因此,TensorFlow、PyTorch 都支援 Dataset/DataLoader,一次只讀取一批(batch)資料進行訓練,完成後在讀取下一批資料訓練,這樣才能節省記憶體,接下來就來看看兩個套件的作法。

MNIST轉Dataset

TensorFlow:載入MNIST資料,格式為 NumPy 陣列,之後經過前置處理再轉為Dataset。

import tensorflow as tf

mnist = tf.keras.datasets.mnist

# 載入 MNIST 手寫阿拉伯數字資料
(x_train, y_train),(x_test, y_test) = mnist.load_data()

# 特徵縮放,使用常態化(Normalization),公式 = (x - min) / (max - min)
x_train_norm, x_test_norm = x_train / 255.0, x_test / 255.0

# 轉為 Dataset,含 X/Y 資料
train_ds = tf.data.Dataset.from_tensor_slices((x_train_norm, y_train))

PyTorch:載入MNIST資料,直接經轉為Dataset,可透過transform轉為PyTorch張量或進行前置處理。

import torch
from torch.utils.data import DataLoader
from torchvision import transforms
from torchvision.datasets import MNIST

# 下載 MNIST 手寫阿拉伯數字 訓練資料
train_ds = MNIST('', train=True, download=True, 
                 transform=transforms.ToTensor())

transform進行前置處理的實作如下,PyTorch直接利用transform進行標準化(Standardization)轉換,公式可參閱Scikit-learn

# 資料轉換
transform = transforms.Compose(
    [transforms.ToTensor(),
     # 讀入圖像範圍介於[0, 1]之間,將之轉換為 [-1, 1]
     transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))
     # ImageNet
     # transforms.Normalize((0.485, 0.456, 0.406), (0.229, 0.224, 0.225))
    ])
    
# 載入資料集,如果出現 BrokenPipeError 錯誤,將 num_workers 改為 0
train_ds = torchvision.datasets.CIFAR10(root='./CIFAR10', train=True,
                                        download=True, transform=transform)    

讀取檔案轉Dataset

通常會讀取目錄內所有檔案,作為訓練或測試資料。
TensorFlow:

train_ds = tf.keras.preprocessing.image_dataset_from_directory(
    './data/training_set',
    validation_split=0.2,
    subset="training",
    seed=1337,
    image_size=image_size,
    batch_size=batch_size,
)

PyTorch:

import torchvision

train_ds = torchvision.datasets.ImageFolder('./data/training_set', transform=transform)

均以次目錄名稱作為標註(Label),例如以下結構:
https://ithelp.ithome.com.tw/upload/images/20220605/20001976snNOTIKtYt.png

如果不是以上結構,PyTorch 也可以自訂Dataset類別,只要實作__init__、len、__getitem__三個方法,程式碼如下,完整範例可參考開發者傳授 PyTorch 秘笈 的src/06_05_Data_Augmentation_MNIST.ipynb:

class CustomImageDataset(torch.utils.data.Dataset):
    def __init__(self, img_dir, transform=None, target_transform=None
                 , to_gray=False, size=28):
        self.img_labels = [file_name for file_name in os.listdir(img_dir)]
        self.img_dir = img_dir
        self.transform = transform
        self.target_transform = target_transform
        self.to_gray = to_gray
        self.size = size

    def __len__(self):
        return len(self.img_labels)

    def __getitem__(self, idx):
        # 組合檔案完整路徑
        img_path = os.path.join(self.img_dir, self.img_labels[idx])
        # 讀取圖檔
        mode = 'L' if self.to_gray else 'RGB'
        image = Image.open(img_path, mode='r').convert(mode)
        image = Image.fromarray(1.0-(np.array(image)/255))

        # print(image.shape)
        # 去除副檔名
        label = int(self.img_labels[idx].split('.')[0])
        
        # 轉換
        if self.transform:
            image = self.transform(image)
        if self.target_transform:
            label = self.target_transform(label)
        
        return image, label

TensorFlow 作法可參閱Creating Custom TensorFlow Dataset

Dataset 轉 DataLoader

TensorFlow:直接將Dataset餵入模型訓練即可,不需DataLoader。

# 模型訓練
model.fit(
    train_ds, epochs=epochs, validation_data=val_ds
)

PyTorch:需轉為DataLoader,再餵入模型訓練。

data_loader = torch.utils.data.DataLoader(train_ds, batch_size=10,shuffle=False)

def train(model, device, train_loader, criterion, optimizer, epoch):
    model.train()
    loss_list = []    
    for batch_idx, (data, target) in enumerate(train_loader):
        data, target = data.to(device), target.to(device)
        
        optimizer.zero_grad()
        output = model(data)
        loss = criterion(output, target)
        loss.backward()
        optimizer.step()
        
        if (batch_idx+1) % 10 == 0:
            loss_list.append(loss.item())
            batch = (batch_idx+1) * len(data)
            data_count = len(train_loader.dataset)
            percentage = (100. * (batch_idx+1) / len(train_loader))
            print(f'Epoch {epoch}: [{batch:5d} / {data_count}] ' +
                  f'({percentage:.0f} %)  Loss: {loss.item():.6f}')
    return loss_list
    
for epoch in range(1, epochs + 1):
    loss_list += train(model, device, train_loader, criterion, optimizer, epoch)

結論

TensorFlow/PyTorch 基本設計概念是一致的,只是有些細節是存在差異的,例如 TensorFlow Dataset 可以使用cache、prefetch 縮短訓練時間。

下一篇我們繼續比較模型訓練的細節。

以下為工商廣告:)。
PyTorch:
開發者傳授 PyTorch 秘笈
https://ithelp.ithome.com.tw/upload/images/20220531/20001976MhL9K2rsgO.png
預計 2022/6/20 出版。

TensorFlow:
深度學習 -- 最佳入門邁向 AI 專題實戰
https://ithelp.ithome.com.tw/upload/images/20220531/20001976ZOxC7BHyN3.jpg


圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言